/* Depends: InitClass.js, Control.js, List.js
 * 
 */
NJS.MultiSelect = NJS.QueryableDropdownSelect.extend({
    init: function (options) {
        var instance = this;
        if (this._setOptions(options) === this.INVALID) {
            return this.INVALID
        }
        NJS.$(this.options.element).hide();
        if (this.options.disabled) {
            this._createFurniture(true);
            return this
        }
        this.model = new NJS.SelectModel({
            element: this.options.element,
            removeOnUnSelect: this.options.removeOnUnSelect
        });
        this._createFurniture();
        this.dropdownController = NJS.InlineLayer.create({
            alignment: NJS.LEFT,
            offsetTarget: this.$field,
            content: $(".aui-list", this.$container)
        });
        this.listController = new NJS.List({
            containerSelector: $(".aui-list", this.$container),
            groupSelector: "ul.aui-list-section",
            itemSelector: "li",
            matchingStrategy: this.options.matchingStrategy,
            selectionHandler: function (e) {
                instance._selectionHandler(this.getFocused(), e);
                return false
            }
        });
        this._assignEventsToFurniture();
        this._setTextareaDimThresholds();
        this.lozengeGroup = new NJS.MultiSelect.LozengeGroup();
        this._assignEvents("lozengeGroup", this.lozengeGroup);
        this._restoreSelectedOptions();
        return this
    },
    _getDefaultOptions: function () {
        return $.extend(true, this._super(), {
            minRoomForText: 50,
            errorMessage: NJS.params.multiselectGenericError,
            ajaxOptions: {
                minQueryLength: 1
            },
            showDropdownButton: true
        })
    },
    _createFurniture: function (disabled) {
        var id = this.model.$element.attr("id");
        if (this.model.$element.prev().hasClass("NJS-multi-select-placeholder")) {
            this.model.$element.prev().remove()
        }
        if (disabled) {
            this.model.$element.replaceWith(this._render("disableSelectField", id))
        } else {
            this.$container = this._render("container", id);
            this.$field = this._render("field", id).appendTo(this.$container);
            this.$container.append(this._render("suggestionsContainer", id));
            this.$container.insertBefore(this.model.$element);
            this.$dropDownIcon = this._render("dropdownAndLoadingIcon", this._hasDropdownButton()).appendTo(this.$container);
            this.$errorMessage = this._render("errorMessage");
            this.$selectedItemsWrapper = this._render("selectedItemsWrapper").appendTo(this.$container);
            this.$selectedItemsContainer = this._render("selectedItemsContainer").appendTo(this.$selectedItemsWrapper)
        }
    },
    _assignEventsToFurniture: function () {
        this._super();
        this._assignEvents("body", document);
        this._assignEvents("selectedItemsContainer", this.$selectedItemsContainer)
    },
    _getUserInputValue: function () {
        return this.options.uppercaseUserEnteredOnSelect ? this.$field.val().toUpperCase() : this.$field.val()
    },
    _handleUserInputOption: function () {
        var groupDescriptor;
        if (!this.hasUserInputtedOption() || this.$field.val().length === 0) {
            return
        }
        groupDescriptor = new NJS.GroupDescriptor({
            type: "optgroup",
            label: "user inputted option",
            weight: 9999,
            showLabel: false,
            replace: true
        });
        groupDescriptor.addItem(new NJS.ItemDescriptor({
            value: this._getUserInputValue(),
            label: this.$field.val(),
            labelSuffix: " (" + this.options.userEnteredOptionsMsg + ")",
            title: this.$field.val(),
            allowDuplicate: false
        }));
        this.model.appendOptionsFromJSON([groupDescriptor])
    },
    hasUserInputtedOption: function () {
        return this.options.userEnteredOptionsMsg
    },
    _handleSuggestionResponse: function (data) {
        if (this.options.ajaxOptions.query) {
            this.model.clearUnSelected()
        }
        this._handleUserInputOption();
        this._super(data)
    },
    _setSuggestions: function (data) {
        if (data) {
            this.model.appendOptionsFromJSON(data);
            this._super(this.model.getUnSelectedDescriptors())
        } else {
            this.hideSuggestions()
        }
    },
    removeItem: function (descriptor) {
        this.model.setUnSelected(descriptor);
        this.updateItemsIndent()
    },
    _restoreSelectedOptions: function () {
        var instance = this;
        $.each(this.model.getSelectedDescriptors(), function () {
            instance._addItem(this)
        });
        this.updateItemsIndent()
    },
    _shouldEnableLozengeGroup: function () {
        return this.lozengeGroup.items.length > 0 && this.lozengeGroup.index < 0 && (this.$field.val().length === 0 || this.getCaret(this.$field[0]) === 0)
    },
    _handleBackSpace: function () {
        var instance = this;
        if (this._shouldEnableLozengeGroup()) {
            setTimeout(function () {
                instance.lozengeGroup.shiftFocus(-1)
            }, 0)
        } else {
            this.$field.one("keyup", function () {
                instance._handleCharacterInput()
            })
        }
    },
    _handleDelete: function () {
        if ($.trim(this.$field.val()) !== "") {
            var instance = this;
            this.$field.one("keyup", function () {
                instance._handleCharacterInput()
            })
        }
    },
    _handleLeft: function () {
        if (this._shouldEnableLozengeGroup()) {
            var instance = this;
            setTimeout(function () {
                instance.lozengeGroup.shiftFocus(-1)
            }, 0)
        }
    },
    _handlePaste: function () {
        this.$field.val($.trim(this.$field.val()).replace(/\s+/g, " "));
        this._handleCharacterInput()
    },
    _setTextareaDimThresholds: function () {
        this.maxWidth = this.options.maxWidth || this.$container.attr("scrollWidth");
        this.minWidth = this.options.minWidth || this.$container.attr("scrollWidth");
        if (this.minWidth > this.maxWidth) {
            this.minWidth = this.maxWidth
        }
    },
    updateItemsIndent: function () {
        var inputIndent = this._getInputIndent();
        this.$field.css({
            paddingTop: inputIndent.top,
            paddingLeft: inputIndent.left,
            width: 0
        });
        this.$field.css({
            width: this.$container.width() - inputIndent.left - 21
        });
        if (this.currentTopOffset && this.currentTopOffset !== inputIndent.top) {
            this.$container.trigger("multiSelectHeightUpdated", [this])
        }
        if ($.browser.msie) {
            this.$field.val(this.$field.val() + " ");
            this.$field.val(this.$field.val().replace(/\s$/, ""))
        }
        this.currentTopOffset = inputIndent.top
    },
    _isItemPresent: function (descriptor) {
        var duplicate = false;
        var label = descriptor.label();
        $.each(this.lozengeGroup.items, function () {
            if (this.options.label === label) {
                duplicate = true;
                return false
            }
        });
        return duplicate
    },
    _addItem: function (descriptor) {
        if (descriptor instanceof NJS.ItemDescriptor) {
            descriptor = NJS.copyObject(descriptor.allProperties(), false)
        }
        descriptor.value = $.trim(descriptor.value);
        descriptor.label = $.trim(descriptor[this.options.itemAttrDisplayed]) || descriptor.value;
        descriptor.title = $.trim(descriptor.title) || descriptor.label;
        descriptor = new NJS.ItemDescriptor(descriptor);
        if (this._isItemPresent(descriptor)) {
            return
        }
        var lozenge = new NJS.MultiSelect.Lozenge({
            label: descriptor.label(),
            title: descriptor.title(),
            container: this.$selectedItemsContainer
        });
        this.lozengeGroup.addItem(lozenge);
        this._assignEvents("lozenge", lozenge);
        this.model.setSelected(descriptor);
        this.updateItemsIndent();
        this.dropdownController.setPosition()
    },
    _addMultipleItems: function (items, removeOnUnSelect) {
        var instance = this;
        $.each(items, function (i, descriptor) {
            if (removeOnUnSelect) {
                descriptor.removeOnUnSelect = true
            }
            instance._addItem(descriptor)
        })
    },
    _getTargetItemContainerWidth: function () {
        var lozenges = this.lozengeGroup.items,
            width = parseInt(this.$selectedItemsContainer.css("paddingLeft"), 10) + parseInt(this.$selectedItemsContainer.css("paddingRight"), 10);
        for (var i = lozenges.length - 1; i >= 0; i--) {
            var $item = lozenges[i].$lozenge;
            width = width + $item.outerWidth() + parseInt($item.css("marginLeft"), 10) + parseInt($item.css("marginRight"), 10)
        }
        return width
    },
    _getInputIndent: function () {
        var top, left, iconArea = 22,
            paddingLeft = 2,
            paddingTop = 4,
            indent = {
                top: paddingTop,
                left: paddingLeft
            },
            lastLozengeIndex = this.lozengeGroup.items.length - 1,
            $last;
        if (lastLozengeIndex >= 0) {
            $last = this.lozengeGroup.items[lastLozengeIndex].$lozenge;
            top = $last.attr("offsetTop");
            left = $last.attr("offsetLeft") + $last.outerWidth();
            if (left > this.$container.width() - iconArea - this.options.minRoomForText) {
                top += $last.attr("offsetHeight");
                left = 0
            }
            indent.top += top;
            indent.left += left
        }
        return indent
    },
    _selectionHandler: function (selected, e) {
        var instance = this;
        selected.each(function () {
            instance._addItem($.data(this, "descriptor"))
        });
        this.$field.val("").focus().scrollIntoView({
            margin: 20
        });
        this.hideSuggestions();
        this.hideErrorMessage();
        this.model.$element.trigger("change");
        e.preventDefault()
    },
    isValidItem: function (itemValue) {
        var suggestedItemDescriptor = this.listController.getFocused().data("descriptor");
        if (!suggestedItemDescriptor) {
            return false
        }
        itemValue = itemValue.toLowerCase();
        return itemValue === $.trim(suggestedItemDescriptor.label.toLowerCase()) || itemValue === $.trim(suggestedItemDescriptor.value.toLowerCase())
    },
    showErrorMessage: function (value) {
        this.$errorMessage.text(NJS.format(this.options.errorMessage, value || this.$field.val()));
        var $container = this.$container.parent(".field-group");
        if ($container.length === 0) {
            $container = this.$container.parent(".frother-control-renderer");
            this.$errorMessage.prependTo($container)
        } else {
            this.$errorMessage.appendTo($container)
        }
    },
    hideErrorMessage: function () {
        this.$errorMessage.remove()
    },
    handleFreeInput: function () {
        var value = $.trim(this.$field.val()),
            descriptor;
        if (value) {
            descriptor = this.model.getDescriptor(value);
            if (descriptor) {
                this._addItem(descriptor);
                this.model.$element.trigger("change")
            } else {
                this.showErrorMessage(value);
                return
            }
        }
        this.hideErrorMessage();
        this.$field.val("")
    },
    submitForm: function () {
        if (this.$field.val().length === 0 && !this.suggestionsVisible) {
            $(this.$field[0].form).submit()
        }
    },
    _deactivate: function () {
        this.handleFreeInput();
        this.lozengeGroup.trigger("blur");
        this.hideSuggestions()
    },
    keys: {
        left: function () {
            this._handleLeft()
        },
        backspace: function () {
            this._handleBackSpace()
        },
        del: function () {
            this._handleDelete()
        },
        "return": function (e) {
            this.submitForm();
            e.preventDefault()
        }
    },
    _events: {
        body: {
            tabSelect: function () {
                if (this.$field.is(":visible")) {
                    this._setTextareaDimThresholds();
                    this.updateItemsIndent()
                }
            },
            bulkTabSelect: function () {
                if (this.$field.is(":visible")) {
                    this._setTextareaDimThresholds();
                    this.updateItemsIndent()
                }
            }
        },
        field: {
            paste: function () {
                setTimeout($.proxy(this, "_handlePaste"), 0)
            },
            "keydown keypress": function (event) {
                if (this.lozengeGroup.index >= 0) {
                    if (JIRA.Keyboard.SpecialKey.fromKeyCode(event.keyCode) in this.lozengeGroup.keys) {
                        event.preventDefault()
                    } else {
                        if (JIRA.Keyboard.SpecialKey.fromKeyCode(event.keyCode) === "return") {
                            this.submitForm();
                            event.preventDefault()
                        } else {
                            var instance = this;
                            this.$field.one("keyup", function () {
                                instance._handleCharacterInput()
                            });
                            this.lozengeGroup.trigger("blur")
                        }
                    }
                }
            },
            click: function () {
                this.lozengeGroup.trigger("blur");
                this.$field.focus()
            }
        },
        lozengeGroup: {
            focus: function () {
                this.$field.focus();
                this.hideSuggestions();
                this._unassignEvents("keys", this.$field)
            },
            blur: function () {
                this._assignEvents("keys", this.$field);
                if (this.$field.val()) {
                    this._handleCharacterInput()
                }
            }
        },
        lozenge: {
            remove: function (event) {
                this.removeItem(this.model.getDescriptor(event.target.options.label))
            }
        },
        selectedItemsContainer: {
            click: function (event) {
                if (event.target === event.currentTarget) {
                    this.lozengeGroup.trigger("blur");
                    this.$field.focus()
                }
            }
        }
    },
    _renders: {
        errorMessage: function () {
            return $('<div class="error" />')
        },
        selectedItemsWrapper: function () {
            return $('<div class="representation"></div>')
        },
        selectedItemsContainer: function () {
            return $('<ul class="items" />')
        },
        field: function (idPrefix) {
            return $('<textarea autocomplete="off" id="' + idPrefix + '-textarea" class="aui-field" wrap="off"></textarea>')
        },
        disableSelectField: function (id) {
            return $("<input type='text' class='long-field' name='" + id + "' id='" + id + "' />")
        },
        container: function (idPrefix) {
            return $('<div class="multi-select" id="' + idPrefix + '-multi-select">')
        },
        suggestionsContainer: function (idPrefix) {
            return $('<div class="aui-list" id="' + idPrefix + '-suggestions" tabindex="-1"></div>')
        }
    }
});
NJS.MultiSelect.Lozenge = NJS.Control.extend({
    init: function (options) {
        this._setOptions(options);
        this.$lozenge = this._render("lozenge");
        this.$removeButton = this._render("removeButton");
        this._assignEvents("instance", this);
        this._assignEvents("lozenge", this.$lozenge);
        this._assignEvents("removeButton", this.$removeButton);
        this.$removeButton.appendTo(this.$lozenge);
        this.$lozenge.appendTo(this.options.container)
    },
    _getDefaultOptions: function () {
        return {
            label: null,
            title: null,
            container: null,
            focusClass: "focused"
        }
    },
    _renders: {
        "lozenge": function () {
            var label = NJS.escapeHTML(this.options.label);
            var title = NJS.escapeHTML(this.options.title) || "";
            return $('<li class="item-row" title="' + title + '"><button type="button" tabindex="-1" class="value-item"><span><span class="value-text">' + label + "</span></span></button></li>")
        },
        "removeButton": function () {
            return $('<em class="item-delete" title="' + NJS.escapeHTML(NJS.params.removeItem) + '"></em>')
        }
    },
    _events: {
        "instance": {
            "focus": function () {
                this.$lozenge.addClass(this.options.focusClass)
            },
            "blur": function () {
                this.$lozenge.removeClass(this.options.focusClass)
            },
            "remove": function () {
                this.$lozenge.remove()
            }
        },
        "lozenge": {
            "click": function () {
                this.trigger("focus")
            }
        },
        "removeButton": {
            "click": function () {
                this.trigger("remove")
            }
        }
    }
});
NJS.MultiSelect.LozengeGroup = NJS.Group.extend({
    keys: {
        "left": function () {
            if (this.index > 0) {
                this.shiftFocus(-1)
            }
        },
        "right": function () {
            if (this.index === this.items.length - 1) {
                this.items[this.index].trigger("blur")
            } else {
                this.shiftFocus(1)
            }
        },
        "backspace": function () {
            var index = this.index;
            if (index > 0) {
                this.shiftFocus(-1)
            } else {
                if (this.items.length > 1) {
                    this.shiftFocus(1)
                }
            }
            this.items[index].trigger("remove")
        },
        "del": function () {
            var index = this.index;
            if (index + 1 < this.items.length) {
                this.shiftFocus(1)
            }
            this.items[index].trigger("remove")
        }
    }
});